#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <set>

const int line_size = 1024;

class Command
{
public:
    //限定可执行的命令，要求严格拼配
    Command()
    {
        _white_list.insert("ls");
        _white_list.insert("ls -l");
        _white_list.insert("ll");
        _white_list.insert("pwd");
        _white_list.insert("touch");
        _white_list.insert("who");
        _white_list.insert("whoami");
    }

    bool SafeCheck(const std::string &cmdstr)
    {
        auto iter = _white_list.find(cmdstr);
        return iter == _white_list.end() ? false : true;
    }

    //给定一个命令字符串"ls -l"，让其执行并把执行结果返回
    std::string Execute(std::string cmdstr)
    {
        // 1. pipe
        // 2. fork + dup2(pipe[1], 1) + exec*, 子进程将执行结果交给父进程，pipe[0]
        // 3. return
        // popen等价于上面的操作
        // FILE *popen(const char *command, const char *type);
        // int pclose(FILE *stream);

        //检查该键值是否合法
        if(!SafeCheck(cmdstr))
        {
            return std::string(cmdstr + "不支持");
        }

        //使用popen系统接口来使用命令
        FILE *fp = ::popen(cmdstr.c_str(), "r");
        if(nullptr == fp)
        {
            return std::string("Failed");
        }
        char buffer[line_size];    //buffer用于放置存放在fp中命令执行后的结果
        std::string result;        //result用于存放命令执行的结果对应的字符串
        while(true)
        {
            //fgets用于读取文件流对象中的数据，并将读取的数据放入指定的缓冲区，返回值为空时表示无数据可再读
            //由于fgets每次只能读取一行，因此外部需要循环包裹
            char *ret = ::fgets(buffer, sizeof(buffer), fp);
            if(!ret) break;
            result += ret;
        }

        pclose(fp);
        return result.empty() ? std::string("Done") : result;
    }
private:
    std::set<std::string> _white_list;
};

